{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- [中文教程](#在pynq中实现pp4fpga算法教程)\n",
    "- [English tutorial](#Tutorial-for-algorithms-in-pp4fpga-with-pynq-board)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "# 在pynq中实现pp4fpga算法教程 \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "*（以下教程提供一种可行的方案，读者可以通过pynq官方文档和xilinx大学计划提供的其他教程探索其他实现方式）*\n",
    "\n",
    "\n",
    "*（教程和所有工程都在2017.4版本下运行）*\n",
    "\n",
    "\n",
    "基于在pp4fpga-cn的github目录中提供的代码示例我们需要以下几个步骤：\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "1. 在Vivado HLS中修改C/C++代码以生成可以接入block design的HLS算法IP核\n",
    "2. 在Vivado中设计block design\n",
    "3. Vivado将生成的.bit和.tcl文件拷入sd卡\n",
    "4. 在pynq的jupyter notebook中写这个算法的python驱动\n",
    "\n",
    "以下我们以哈夫曼编码作为示范:\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.在Vivado HLS中修改C/C++代码以生成可以接入block design的HLS算法IP核\n",
    "\n",
    "\n",
    "\n",
    "首先我们这里暂时不需要创造握手的控制信号，所以将顶层函数的接口类型改为ap_ctrl_none。\n",
    "\n",
    "输入输出的接口这里使用的是axis接口（axi stream），注意axis接口要求读/写顺序进行，如果某函数的读写不是顺序进行，可以建立一个temp数组顺序读入后操作或可以使用其他接口。\n",
    "\n",
    "有时候片上资源会不够用，这时候需要对函数做一些优化以节省资源，例如for循环pipeline，具体每个算法的优化方式可以在pp4fpga的书中找到。\n",
    "\n",
    "修改后的directive面板如下图所示。\n",
    "\n",
    "\n",
    "![](./data/directive.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.在Vivado中设计block design\n",
    "\n",
    "我们将生成的HLS IP添加到Vivado工程的IP目录下。\n",
    "\n",
    "可以看到哈夫曼编码的IP有两个输入，两个输出，全部都是stream接口。stream接口需要DMA（AXI Direct Memory Access）与PS相连，所以我们再添加两个DMA。\n",
    "\n",
    "通过AXI Interconnect与PS相连，然后分配读写的路线，剩余内容可以auto-connect。\n",
    "\n",
    "最终的block design如下图所示。\n",
    "\n",
    "\n",
    "![](./data/block.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3.Vivado将生成的.bit和.tcl文件拷入sd卡\n",
    "\n",
    "完成block design后我们点击generate bitstream，生成这个设计的比特流文件。\n",
    "\n",
    "打开Vivado里的Tcl Console，使用命令 “write_bd_tcl <路径>” 生成tcl文件。\n",
    "\n",
    "将.bit和.tcl文件改成一样的名字，将他们放入sd卡。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4.在pynq的jupyter notebook中写这个算法的python驱动\n",
    "\n",
    "打开pynq的jupyter notebook，用python编写这个程序的python驱动。\n",
    "\n",
    "教程的示例中提供的是stream接口的驱动的编写方式，其他例如lite接口驱动的示例可以从其他Xilinx大学计划的教程中获取。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial for algorithms in pp4fpga with pynq board"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*(This Tutorial provides one of the methods to to achieve the algorithms in the book and readers can explore other possible ways through offcial pynq documents and other Xilinx University Program tutorials.)*\n",
    "\n",
    "*(All the tutorial source files are under Vivado/Vivado HLS 2017.4 version.)*\n",
    "\n",
    "\n",
    "With the code examples in pp4fpga-cn github repository(pp4fpga), we need following steps to build a project:\n",
    "\n",
    "\n",
    "1. Revise the C/C++ code to create interfaces that can connect our HLS algorithm IP cores to the block design.\n",
    "2. Finish the block design in Vivado.\n",
    "3. Copy the .bit and .tcl file generated by Vivado to our sd card.\n",
    "4. Write the project driver with python in pynq's jupyter notebook.\n",
    "\n",
    "\n",
    "A example of achieving Huffman Encoding is provided as followed:\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Revise the C/C++ code to create interfaces that can connect our HLS algorithm IP cores to the block design\n",
    "\n",
    "We don't need to create handshake signals for now so first top function's interface type should be changed to ap_ctrl_none.\n",
    "\n",
    "We would use axis(axi stream) as input/output's interface type. Notice that axis interface requires that reading and writing have to be in sequences. If certain algorithms' r/w is not sequential, one easy solution is to create a temp array to read the input and do the manipulation on the temp array.\n",
    "\n",
    "Sometimes limited on-chip resources ask us to do some optimization first to pass the compilation, for example forloop's pipeline. Readers can find these optimizations illustrated in *pp4fpga*.\n",
    "\n",
    "The directive board after the revision is shown in the following picture.\n",
    "\n",
    "\n",
    "\n",
    "![](./data/directive.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Finish the block design in Vivado.\n",
    "\n",
    "\n",
    "Then we need to add the HLS IP to the IP repository of our Vivado project.\n",
    "\n",
    "We can see Huffman Encoding's IP core has two inputs and two outputs, all under stream interfaces now. Stream interfaces need DMA(AXI Direct Memory Access) to be connected with PS. So we will add two DMA blocks.\n",
    "\n",
    "These interfaces are connected to PS through AXI Interconnect, and we need to assign the path for read/write. All the other ports can be handled by auto-connect button.\n",
    "\n",
    "The final block design is shown in the following picture.\n",
    "\n",
    "![](./data/block.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Copy the .bit and .tcl file generated by Vivado to our sd card.\n",
    "\n",
    "Having finished the block design, we can get the .bit file by clickong on generate bitstream button.\n",
    "\n",
    "Open the Tcl Console in Vivado, type the command \"write_bd_tcl <path>\" to output the tcl file.\n",
    "    \n",
    "Make sure that .bit and .tcl have the same file name, and put them into our sd card."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Write the project driver with python in pynq's jupyter notebook.\n",
    "\n",
    "Open the jupyter notebook in pynq, write the program's driver with python.\n",
    "\n",
    "The example in the tutorial resource is the driver for stream interface. The driver for other interfaces, such as lite interface, can be learned through other Xilinx University Program's tutorials or workshops."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
